using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;

namespace _3Dto2D.Services;

public class Shader : IDisposable
{
    private readonly int _handle;

    public Shader(string vertexPath, string fragmentPath)
    {
        var vertexShaderSource = File.ReadAllText(vertexPath);
        var fragmentShaderSource = File.ReadAllText(fragmentPath);

        var vertexShader = CompileShader(ShaderType.VertexShader, vertexShaderSource);
        var fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentShaderSource);

        _handle = GL.CreateProgram();
        GL.AttachShader(_handle, vertexShader);
        GL.AttachShader(_handle, fragmentShader);
        GL.LinkProgram(_handle);

        GL.GetProgram(_handle, GetProgramParameterName.LinkStatus, out var success);
        if (success == 0)
        {
            var infoLog = GL.GetProgramInfoLog(_handle);
            throw new Exception($"Error linking shader program: {infoLog}");
        }

        GL.DetachShader(_handle, vertexShader);
        GL.DetachShader(_handle, fragmentShader);
        GL.DeleteShader(vertexShader);
        GL.DeleteShader(fragmentShader);
    }

    public void Use()
    {
        GL.UseProgram(_handle);
    }

    public void SetUniform(string name, Matrix4 value)
    {
        var location = GL.GetUniformLocation(_handle, name);
        if (location == -1)
        {
            return;
        }
        GL.UniformMatrix4(location, false, ref value);
    }


    public void SetUniform(string name, Vector4 value)
    {
        var location = GL.GetUniformLocation(_handle, name);
        if (location != -1)
        {
            GL.Uniform4(location, value);
        }
    }

    private int CompileShader(ShaderType type, string source)
    {
        var shader = GL.CreateShader(type);
        GL.ShaderSource(shader, source);
        GL.CompileShader(shader);

        GL.GetShader(shader, ShaderParameter.CompileStatus, out var success);
        if (success == 0)
        {
            var infoLog = GL.GetShaderInfoLog(shader);
            throw new Exception($"Error compiling shader of type {type}: {infoLog}");
        }

        return shader;
    }

    public void Dispose()
    {
        GL.DeleteProgram(_handle);
    }
}
